#pragma once

#include <coroutine>
#include <exception>

// template <class T>
// concept Awaitable =
//     // 要求类型可移动构造（协程挂起后可能移动awaitable对象）
//     std::is_move_constructible_v<std::decay_t<T>> &&
//     // 要求类型提供三个核心方法
//     requires(std::decay_t<T> &&t, std::coroutine_handle<> handle) {
//       // 1. await_ready(): 返回可转换为bool的值，表示是否无需挂起
//       { t.await_ready() } -> std::convertible_to<bool>;
//       // 2. await_suspend(): 协程挂起前调用，参数为协程句柄
//       { t.await_suspend(handle) }; // 返回类型无约束（标准允许任意）
//       // 3. await_resume(): 协程恢复时调用，返回结果（类型任意）
//       { t.await_resume() };
//     };

class PreviousAwaiter {
public:
  PreviousAwaiter(std::coroutine_handle<> previous) : m_previous(previous) {};

  // 结果为false，则暂停协程并调用 awaiter.await_suspend(handle)
  bool await_ready() const noexcept { return false; };
  std::coroutine_handle<>
  await_suspend(std::coroutine_handle<> _) const noexcept {
    if (m_previous) {
      return m_previous;
    }
    return std::noop_coroutine();
  }
  void await_resume() const noexcept {}

private:
  std::coroutine_handle<> m_previous;
};

template <class T> class UnInit {
public:
  UnInit() noexcept {}
  ~UnInit() noexcept {}
  template <class... Args> void put(Args &&...args) {
    new (std::addressof(m_value)) T(std::forward<Args>(args)...);
  }
  T move() {
    T val{std::move(m_value)};
    m_value.~T();
    // std::cout << "move1" << std::endl;
    return std::move(val);
  }

private:
  UnInit(UnInit &&) = delete;
  union {
    T m_value;
  };
};

template <class T> struct Promise {
  auto initial_suspend() { return std::suspend_always(); }

  auto final_suspend() noexcept { return PreviousAwaiter(m_previous); }

  auto get_return_object() {
    return std::coroutine_handle<Promise>::from_promise(*this);
  }

  void unhandled_exception() {
    // std::cout << "unhandled_exception" << std::endl;
    m_exception = std::current_exception();
    std::rethrow_exception(m_exception);
  }

  void return_value(T &&val) { m_result.put(std::move(val)); }
  void return_value(const T &val) { m_result.put(val); }

  T result() {
    if (m_exception) {
      std::rethrow_exception(m_exception);
    }
    // std::cout << "move2" << std::endl;

    return m_result.move();
  }

  std::coroutine_handle<> m_previous;
  std::exception_ptr m_exception;

  Promise &operator=(Promise &&) = delete;
  UnInit<T> m_result;
};
;

template <> struct Promise<void> {
  auto initial_suspend() { return std::suspend_always(); }

  auto final_suspend() noexcept { return PreviousAwaiter(m_previous); }

  auto get_return_object() {
    return std::coroutine_handle<Promise>::from_promise(*this);
  }

  void unhandled_exception() {
    // std::cout << "unhandled_exception" << std::endl;
    m_exception = std::current_exception();
    std::rethrow_exception(m_exception);
  }

  void return_void() {}

  std::coroutine_handle<> m_previous;
  std::exception_ptr m_exception;

  Promise &operator=(Promise &&) = delete;
};

template <class T, class P = Promise<T>>
class [[nodiscard("no co_await")]] Task {
public:
  using promise_type = P;
  Task(std::coroutine_handle<promise_type> co_handle) {
    m_handle = co_handle;
  }
  Task(Task &&that) : m_handle(that.m_handle) {
    that.m_handle = nullptr;
  };
  ~Task() {
    if (m_handle) {
      m_handle.destroy();
    }
  }
  auto operator co_await() const noexcept { return Awaiter{m_handle}; }
  operator std::coroutine_handle<>() const noexcept { return m_handle; }
  auto &get_handle() noexcept { return m_handle; }
  struct Awaiter {
    bool await_ready() const noexcept { return false; }

    std::coroutine_handle<>
    await_suspend(std::coroutine_handle<> coroutine) const noexcept {
      m_handle.promise().m_previous = coroutine;
      return m_handle;
    }

    auto await_resume() const { return m_handle.promise().result(); }
    std::coroutine_handle<promise_type> m_handle;
  };

  std::coroutine_handle<promise_type> m_handle;
};

template <class P> struct GetCoroutine {
  bool await_ready() const noexcept { return false; }
  std::coroutine_handle<P>
  await_suspend(std::coroutine_handle<P> pre) noexcept {
    m_previous = pre;
    return pre;
  }
  std::coroutine_handle<P> await_resume() noexcept { return m_previous; }
  std::coroutine_handle<P> m_previous;
};